package ru.vsu.cs.pm.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ru.vsu.cs.pm.exceptions.IllegalAmountOfMoney;
import ru.vsu.cs.pm.models.*;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * User: ohodyakov
 * Date: 04.01.2011
 * Time: 12:20:17
 */
@Component
public class DealerShipManagerImpl implements DealerShipManager {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private AccountManager accountManager;

    @Autowired
    private ItemManager itemManager;

    @Override
    @Transactional(readOnly = true)
    public DealerShip getDealerShip(int dealerShipId) {
        return entityManager.find(DealerShip.class, dealerShipId);
    }

    @Override
    @Transactional
    public DealerShip saveOrUpdate(DealerShip dealerShip) {
        DealerShip updatedDealerShip = null;
        if (dealerShip.getId() == null) {
            entityManager.persist(dealerShip);
        } else {
            DealerShip dealerShipToUpdate = getDealerShip(dealerShip.getId());
            if (dealerShipToUpdate != null) {
                updatedDealerShip = entityManager.merge(dealerShip);
            } else {
                //TODO: log warning
            }
        }
        return updatedDealerShip == null ? dealerShip : updatedDealerShip;
    }

    @Override
    @Transactional
    public void transferMoney(DealerShip dealerShip, User user) throws IllegalAmountOfMoney {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("User is null!");
        }

        Account accountFrom = accountManager.getAccount(dealerShip.getAccountFrom().getId());
        Account accountTo = accountManager.getAccount(dealerShip.getAccountTo().getId());

        if (accountFrom == null || accountFrom.getId() == null) {
            throw new IllegalArgumentException("Account from is null!");
        }
        if (accountTo == null || accountTo.getId() == null) {
            throw new IllegalArgumentException("Account to is null!");
        }
        // TODO: find the solution
//        if (accountFrom.getUser().getId() != user.getId() || accountTo.getUser().getId() != user.getId()) {
//            throw new IllegalStateException("Mismatches in user accounts found!");
//        }

        if (accountFrom.getMoney() < dealerShip.getMoney()) {
            throw new IllegalAmountOfMoney("Not enough amount of money on the account from");
        }
        accountFrom.setMoney(accountFrom.getMoney() - dealerShip.getMoney());
        accountManager.saveOrUpdateAccount(accountFrom);
        accountTo.setMoney(accountTo.getMoney() + dealerShip.getMoney());
        accountManager.saveOrUpdateAccount(accountTo);

        Item transferItem = itemManager.getTransferItemForUser(user);
        if (transferItem == null) {
            throw new IllegalStateException("No transfer item for user");
        }
        dealerShip.setStartDate(new Date());
        dealerShip.setEndDate(new Date());
        dealerShip.setItem(transferItem);

        saveOrUpdate(dealerShip);
    }

    @Override
    @Transactional
    public void reserveMoney(DealerShip dealerShip, User user) throws IllegalAmountOfMoney {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("User is null!");
        }

        Account accountFrom = accountManager.getAccount(dealerShip.getAccountFrom().getId());
        Account accountTo = accountManager.getReserveAccountForUser(user);

        if (accountFrom == null || accountFrom.getId() == null) {
            throw new IllegalArgumentException("Account from is null!");
        }
        if (accountTo == null || accountTo.getId() == null) {
            throw new IllegalArgumentException("Account to is null!");
        }
        // TODO: find the solution
//        if (accountFrom.getUser().getId() != user.getId() || accountTo.getUser().getId() != user.getId()) {
//            throw new IllegalStateException("Mismatches in user accounts found!");
//        }

        if (accountFrom.getMoney() < dealerShip.getMoney()) {
            throw new IllegalAmountOfMoney("Not enough amount of money on the account from");
        }
        accountFrom.setMoney(accountFrom.getMoney() - dealerShip.getMoney());
        accountManager.saveOrUpdateAccount(accountFrom);
        accountTo.setMoney(accountTo.getMoney() + dealerShip.getMoney());
        accountManager.saveOrUpdateAccount(accountTo);

        Item reserveItem = itemManager.getReserveItemForUser(user);
        if (reserveItem == null) {
            throw new IllegalStateException("No reserve item for user");
        }
        dealerShip.setStartDate(new Date());
        dealerShip.setEndDate(new Date());
        dealerShip.setItem(reserveItem);
        dealerShip.setAccountTo(accountTo);

        saveOrUpdate(dealerShip);
    }

    @Override
    @Transactional
    public void addIncome(DealerShip dealerShip, User user) {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("User is null!");
        }

        Account accountTo = accountManager.getAccount(dealerShip.getAccountTo().getId());
        Item incomeItem = dealerShip.getItem();
        if (incomeItem == null || incomeItem.getId() == null) {
            throw new IllegalArgumentException("Income item is null!");
        }
        if (incomeItem.getType() != ItemType.INCOME) {
            throw new IllegalArgumentException("Item type is not INCOME!");
        }
        if (accountTo == null || accountTo.getId() == null) {
            throw new IllegalArgumentException("Account to is null!");
        }
        if (incomeItem.getUser().getId().intValue() != user.getId().intValue()) {
            throw new IllegalStateException("Mismatches of item and user found!");
        }

        accountTo.setMoney(accountTo.getMoney() + dealerShip.getMoney());
        accountManager.saveOrUpdateAccount(accountTo);

        dealerShip.setStartDate(new Date());
        dealerShip.setEndDate(new Date());

        saveOrUpdate(dealerShip);
    }

    @Override
    @Transactional
    public void addExpense(DealerShip dealerShip, User user) {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("User is null!");
        }

        Account accountFrom = accountManager.getAccount(dealerShip.getAccountFrom().getId());
        Item expenseItem = dealerShip.getItem();
        
        if (expenseItem == null || expenseItem.getId() == null) {
            throw new IllegalArgumentException("Expense item is null!");
        }
        if (expenseItem.getType() != ItemType.EXPENSE) {
            throw new IllegalArgumentException("Item type is not EXPENSE!");
        }
        if (accountFrom == null || accountFrom.getId() == null) {
            throw new IllegalArgumentException("Account to is null!");
        }
        if (expenseItem.getUser().getId().intValue() != user.getId().intValue()) {
            throw new IllegalStateException("Mismatches of item and user found!");
        }

        accountFrom.setMoney(accountFrom.getMoney() - dealerShip.getMoney());
        accountManager.saveOrUpdateAccount(accountFrom);

        dealerShip.setStartDate(new Date());
        dealerShip.setEndDate(new Date());

        saveOrUpdate(dealerShip);
    }

    @Override
    @Transactional(readOnly = true)
    public List<DealerShip> getMoneyTransfersForUser(User user, int start, int count) {
        Item transferItem = itemManager.getTransferItemForUser(user);
        
        return entityManager.createNamedQuery("dealerShip.get.by.item.id")
                .setParameter("itemId", transferItem.getId())
                .setFirstResult(start)
                .setMaxResults(count)
                .getResultList();
    }

    @Override
    @Transactional(readOnly = true)
    public int getNumberOfUserMoneyTransfers(User user) {
        Item transferItem = itemManager.getTransferItemForUser(user);
        
        return ((Long) entityManager.createNamedQuery("dealerShip.get.count.by.item.id")
                .setParameter("itemId", transferItem.getId())
                .getSingleResult()).intValue();
    }

    @Override
    @Transactional(readOnly = true)
    public List<DealerShip> getDealerShipsForUserByType(User user, List<ItemType> types, int start, int count) {
        return entityManager.createNamedQuery("dealerShip.get.by.user.id.and.by.item.type")
                .setParameter("userId", user.getId())
                .setParameter("types", types)
                .setFirstResult(start)
                .setMaxResults(count)
                .getResultList();
    }

    @Override
    @Transactional(readOnly = true)
    public int getNumberOfDealerShipsForUserByType(User user, List<ItemType> types) {
        return ((Long) entityManager.createNamedQuery("dealerShip.get.count.by.user.id.and.item.type")
                .setParameter("userId", user.getId())
                .setParameter("types", types)
                .getSingleResult()).intValue();
    }
}
